#include <vector>
#include <fstream>
#include <iostream>

using std::vector;
using std::ifstream;

#include "texture/bitmap.h"

#define BITMAP_ID 0x4D42

namespace sheeplib {

	namespace texture {

		const int RGB_BYTE_SIZE = 3;

		Bitmap::Bitmap() {

		}

		Bitmap::~Bitmap() {

		}

		bool Bitmap::LoadTextureFile(const string& filename) {
			BITMAPFILEHEADER fileHeader;
			BITMAPINFOHEADER fileInfoHeader;
			vector<RGBQUAD> colourTable; //For 8 bit images or less

			std::cout << "WordSize: " << sizeof(word) << "\n";
			std::cout << "DWordSize: " << sizeof(dword) << "\n";
			std::cout << "ByteSize: " << sizeof(byte) << "\n";

			ifstream fileIn(filename.c_str(), std::ios::binary);

			if (!fileIn.is_open()) {
				SetErrorString("Could not open bitmap image: " + filename);
				return false;
			}

			fileIn.read(reinterpret_cast<char*>(&fileHeader), sizeof(BITMAPFILEHEADER));

			if (fileHeader.bfType != 19778) {
				std::cout << fileHeader.bfType << std::endl;
				std::cout << fileHeader.bfSize << std::endl;
				SetErrorString("This is not a valid bitmap. Type is not 19778.");
				return false;
			}

			fileIn.read(reinterpret_cast<char*>(&fileInfoHeader), sizeof(BITMAPINFOHEADER));


			//THIS WONT WORK CHANGE LATER! HEIGHT MAY BE NEGATIVE!
			if (!fileInfoHeader.biHeight || !fileInfoHeader.biWidth) {
				SetErrorString("Error reading the bitmap file. Width or Height are not valid.");
				return false;
			}

			GetFormat().m_Height = abs(fileInfoHeader.biHeight);
			GetFormat().m_Width = abs(fileInfoHeader.biWidth);
			GetFormat().m_BitsPerPixel = fileInfoHeader.biBitCount;

			if (GetFormat().m_BitsPerPixel == 24 || GetFormat().m_BitsPerPixel == 8) {
				SetType(GL_RGB);
			} else if (GetFormat().m_BitsPerPixel == 32) {
				SetType(GL_RGBA);
			}
			std::cout << "Height: " << GetFormat().m_Height << std::endl;
			std::cout << "Width: " << GetFormat().m_Width << std::endl;
			std::cout << "Bpp: " << fileInfoHeader.biBitCount << std::endl;

			int numColours = 1 << fileInfoHeader.biBitCount;
			colourTable.resize(numColours);

			//load the palette for 8 bits per pixel
			if(fileInfoHeader.biBitCount == 8) {
				fileIn.read(reinterpret_cast<char*>(&colourTable[0]), sizeof(RGBQUAD) * numColours);
			}

			dword size = fileHeader.bfSize - fileHeader.bfOffBits;

			std::cout << size << std::endl;

			vector<byte> tempData(size); //Temporarily store the read data

			//Read the image data in
			fileIn.read(reinterpret_cast<char*>(&tempData[0]), sizeof(byte) * size);

			long byteWidth, padWidth;

			byteWidth = padWidth = (long)((float)GetFormat().m_Width * (float) fileInfoHeader.biBitCount / 8.0);

			//add any extra space to bring each line to a DWORD boundary
		    while(padWidth % 4 != 0) {
				padWidth++;
			}

			if(fileInfoHeader.biBitCount == 24) {
				Convert24(tempData, padWidth, byteWidth, size);
			} else if (fileInfoHeader.biBitCount == 8) {
				Convert8(tempData, colourTable, padWidth, byteWidth, size);
			} else {
				SetErrorString("Image depth not supported!");
				return false;
			}

			return true;
		}

		bool Bitmap::Convert24(vector<byte>& tempData, long padWidth, long byteWidth, dword dataSize) {
			int offset, diff;
			int width = GetFormat().m_Width;
			int height = GetFormat().m_Height;

			diff = width * height * RGB_BYTE_SIZE;

			GetFormat().m_ImageData.resize(diff);

			vector<unsigned char>& data = GetFormat().m_ImageData;

			if(height > 0) {
				offset = padWidth - byteWidth;
				//count backwards so you start at the front of the image
				for(int i = 0; i < dataSize; i+=3) {
					//jump over the padding at the start of a new line
					if((i + 1) % padWidth == 0) {
						i += offset;
					}

					data[i + 2] = tempData[i];
					data[i + 1] = tempData[i + 1];
					data[i] = tempData[i + 2];
				}
			} else {
				offset = padWidth - byteWidth;
				int j = dataSize-3;

				//count backwards so you start at the front of the image
				//here you can start from the back of the file or the front,
				//after the header  The only problem is that some programs
				//will pad not only the data, but also the file size to
				//be divisible by 4 bytes.
				for(int i = 0;i < dataSize; i+=3) {
					//jump over the padding at the start of a new line
					if((i + 1) % padWidth == 0) {
						i += offset;
					}

					data[j + 2] = tempData[i];
					data[j + 1] = tempData[i + 1];
					data[j] = tempData[i + 2];

					j-=3;
				}
			}

			return true;
		}

		bool Bitmap::Convert8(vector<byte>& tempData, vector<RGBQUAD>& colourTable, long padWidth, long byteWidth, dword dataSize) {
			int offset, diff;
			int width = GetFormat().m_Width;
			int height = GetFormat().m_Height;

			diff = width * height * RGB_BYTE_SIZE;

			GetFormat().m_ImageData.resize(diff);

			vector<unsigned char>& data = GetFormat().m_ImageData;

			if(height > 0) {
				offset = padWidth - byteWidth;
				int j = 0;
				//count backwards so you start at the front of the image
				for(int i = 0; i < dataSize * RGB_BYTE_SIZE; i+=3) {
					//jump over the padding at the start of a new line
					if((i + 1) % padWidth == 0) {
						i += offset;
					}

					data[i] = colourTable[tempData[j]].rgbRed;
					data[i + 1] = colourTable[tempData[j]].rgbGreen;
					data[i + 2] = colourTable[tempData[j]].rgbBlue;
					j++;
				}
			} else {
				offset = padWidth - byteWidth;
				int j = dataSize - 1;
				//count backwards so you start at the front of the image
				for(int i = 0; i < dataSize * RGB_BYTE_SIZE; i+=3) {
					//jump over the padding at the start of a new line
					if((i + 1) % padWidth == 0) {
						i += offset;
					}

					data[i] = colourTable[tempData[j]].rgbRed;
					data[i + 1] = colourTable[tempData[j]].rgbGreen;
					data[i + 2] = colourTable[tempData[j]].rgbBlue;
					j--;

				}
			}

			return true;

		}

	}
}
